pub mod sample;
pub mod fp_kvstore;
pub mod sf_kvstore;
pub mod error;

use sled::Db;

use crate::constants::{INDEX_SF_STORE_NAME, DefaultChunkPointer, INDEX_SF_KEYS};
use crate::superfeature::SuperFeature;
use crate::{constants::{DefaultKvStore, DefaultFingerprintType, INDEX_FP_STORE_NAME}, recipe::{ChunkPointer, FpPointer}};
use std::{path::Path, fs::create_dir_all};

use fp_kvstore::{ChunkInfo,FpKvStore};
use sf_kvstore::SfKvStore;
use error::{IndexResult, IndexError};
//use ethbloom::{Bloom,Input};
//use parking_lot::Mutex;

pub struct IndexStore {
    sf_num:usize,
    fp_store:Db,
    _sf_store:Db,
    feature_trees:Vec<sled::Tree>,
}

impl IndexStore {
	pub fn from_path<P: AsRef<Path>+?Sized>(path:&P,sf_num:usize) -> IndexResult<Self> {
		let path = path.as_ref();
		create_dir_all(path).map_err(|e|IndexError::PathCreate(e))?;
        let fp_store = sled::open(&path.join(INDEX_FP_STORE_NAME))
                            .map_err(|e|IndexError::DbOpen(e))?;
        let sf_store = sled::open(&path.join(INDEX_SF_STORE_NAME))
                            .map_err(|e|IndexError::DbOpen(e))?;
        let mut feature_trees = Vec::new();
        for key in &INDEX_SF_KEYS[0..sf_num] {
            feature_trees.push(sf_store.open_tree(&key).map_err(|e|IndexError::DbOpen(e))?);
        }
        Ok(Self {
            sf_num:sf_num,
            fp_store:fp_store,
            _sf_store:sf_store,
            feature_trees:feature_trees,
        })
    }

    pub fn fp_store(&self) -> &DefaultKvStore {
        &self.fp_store
    }

    pub fn insert_with_fp(&self,fp:&DefaultFingerprintType,info:&ChunkInfo) -> IndexResult<()> {
        self.fp_store().insert_with_fp(fp,info)
    }

    pub fn find_with_fp<T:ChunkPointer>(&self,fp:&DefaultFingerprintType) -> IndexResult<Option<Box<T>>> {
        self.fp_store().find_with_fp(fp)
    }

    pub fn insert_with_sf(&self,sf:&SuperFeature,chunk_pointer:&DefaultChunkPointer) -> IndexResult<()> {
        for (tree,feature) in self.feature_trees.iter().zip(sf.features(self.sf_num).iter()) {
            tree.insert_with_feature(*feature, chunk_pointer)?;
        }
        Ok(())
    }

    pub fn find_with_sf(&self,sf:&SuperFeature) -> IndexResult<Option<Box<DefaultChunkPointer>>> {
        let mut result:Vec<Box<FpPointer>> = Vec::new();
        for (tree,feature) in self.feature_trees.iter().zip(sf.features(self.sf_num).iter()) {
            let chunk_pointer = tree.find_with_feature(*feature)?;
            if let Some(chunk_pointer) = chunk_pointer {
                for other in &result {
                    if chunk_pointer.equal_fp(&other) {
                        return Ok(Some(chunk_pointer));
                    }
                }
                result.push(chunk_pointer);
            }
        }
        Ok(result.pop())
    }
}

use util::FingerPrintHasher;
pub fn fp_store_test(path:&Path) -> Result<(),Box<dyn std::error::Error>> {
    let index_store = IndexStore::from_path(path,3)?;
    let chunks:[[u8;7];4] = [
        [0,1,2,3,4,5,6],
        [1,2,3,4,5,6,7],
        [0,1,2,3,4,5,6],
        [2,3,4,5,6,7,8],
    ];
    for (i,c) in chunks.iter().enumerate() {
        let info:u64 = i.try_into().unwrap();
        let fp = <DefaultFingerprintType as FingerPrintHasher>::fp(c); 
        index_store.insert_with_fp(&fp, &info)?;
        let chunk_pointer = index_store.find_with_fp::<FpPointer>(&fp);
        println!("{i} {:?}",chunk_pointer);
    }
    Ok(())
}